import axios, {
  AxiosError,
  type AxiosInstance,
  type AxiosRequestConfig,
  type AxiosResponse,
  HttpStatusCode,
  type InternalAxiosRequestConfig
} from 'axios'
import { CacheManager } from '@/cache'
import qs from 'qs'
import { useSystemStore } from '@/stores/system'
import pinia from '@/stores'
import type { Event } from '@/types/axios'

const systemStore = useSystemStore(pinia)

// 配置新建一个 axios 实例
const oriAxiosInstance: AxiosInstance = axios.create({
  // baseURL: JSON.parse(import.meta.env.VITE_USE_PROXY)
  baseURL: import.meta.env.VITE_USE_PROXY
    ? import.meta.env.VITE_BASE_API
    : import.meta.env.VITE_API_URL,
  timeout: 60000,
  headers: { 'Content-Type': 'application/json' },
  paramsSerializer: {
    serialize: function (params: Record<string, any>) {
      return qs.stringify(params, { allowDots: true })
    }
  }
})

// 添加请求拦截器
oriAxiosInstance.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 在发送请求之前做些什么 token
    if (CacheManager.getToken()) {
      config.headers['Token'] = `${CacheManager.getToken()}`
    }
    if (config.data instanceof FormData) {
      config.headers['Content-Type'] = 'multipart/form-data'
    }
    return config
  },
  (error: any) => {
    // 对请求错误做些什么
    ElMessage.error(error)
    return Promise.reject(error)
  }
)

// 添加响应拦截器
oriAxiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    if (response.status >= HttpStatusCode.Ok && response.status < HttpStatusCode.MultipleChoices) {
      return response
    } else if (response.status === HttpStatusCode.Unauthorized) {
      CacheManager.clearSession() // 清除浏览器全部临时缓存
      ElMessageBox.alert('你已被登出，请重新登录', '提示', {})
        .then(() => {
          setTimeout(() => {
            window.location.href = '/' // 去登录页
          }, 300)
        })
        .catch(() => {
        })
      return Promise.reject(response)
    } else {
      const result = response?.data
      const message = result?.message
      ElMessage.error(message)
      return Promise.reject(response)
    }
  },
  (error: any) => {
    if (error instanceof AxiosError) {
      ElMessage.error(error.response?.data?.message || error.response?.statusText)
    } else {
      ElMessage.error(error.message || error.code)
    }
    return Promise.reject(error)
  }
)

// 创建一个 Proxy 对象来代理 axios.get
const proxyAxiosInstance = new Proxy(oriAxiosInstance, {
  get(target, prop, receiver) {
    if (prop === 'upload') {
      return async function (...args: any[]) {
        // 获取原始的 axios.post 方法
        const originalPost = target['post']
        // 获取请求参数
        let [url, data, config] = args as [string, any?, AxiosRequestConfig<any>?]
        config = config || {}
        config.headers = config.headers || {}
        config.headers['Content-Type'] = 'multipart/form-data'
        //避免超时报错
        config.timeout = config.timeout === undefined ? 0 : config.timeout
        // 调用原始的 axios.get 方法
        return originalPost(url, data, config)
      }
    } else if (prop === 'download') {
      return async function (...args: any[]) {
        // 获取原始的 axios.get 方法
        const originalGet = target['get']
        // 获取请求参数
        let [url, config] = args as [string, AxiosRequestConfig<any>?]
        config = config || {}
        config.headers = config.headers || {}
        //避免超时报错
        config.timeout = config.timeout === undefined ? 0 : config.timeout
        // 调用原始的 axios.get 方法
        return originalGet(url, config)
      }
    } else if (prop === 'sseRequest') {
      return async function (...args: any[]) {
        // 获取原始的 axios.post 方法
        const originalRequest = target['request']
        // 获取请求参数
        let [config] = args as [AxiosRequestConfig<any>]
        config.headers = config.headers || {}
        config.timeout = config.timeout === undefined ? 0 : config.timeout
        config = {
          ...config,
          adapter: 'fetch',
          responseType: 'stream'
        }
        // 调用原始的 axios.request 方法
        return originalRequest(config).then(async (response: AxiosResponse) => {
          if (config.sseCallback) {
            const stream: ReadableStream = response.data
            const reader = stream.getReader()
            let buffer = ''
            const decoder = new TextDecoder()
            try {
              const events: Array<Event> = []
              while (true) {
                const { value, done } = await reader.read()
                if (done) {
                  break
                }
                // 将 ArrayBuffer 转换为字符串
                const chunk = decoder.decode(value, { stream: true })
                // 处理 SSE 数据
                buffer += chunk
                // 当遇到完整的 SSE 事件时进行处理
                while (buffer.indexOf('\n\n') !== -1) {
                  const eventEnd = buffer.indexOf('\n\n')
                  //一个完整的事件，可能包含event和多个data
                  const eventStr = buffer.slice(0, eventEnd)
                  buffer = buffer.slice(eventEnd + 2)
                  // 解析事件
                  const messages = eventStr.split('\n')
                  // 找到以 "event:" 开头的行
                  const eventNameLine = messages.find((line) => line.startsWith('event:'))
                  //事件名
                  const eventName = eventNameLine ? eventNameLine.substring(6) : ''

                  const idLine = messages.find((line) => line.startsWith('id:'))
                  const id = idLine ? idLine.substring(3) : ''

                  const commentLine = messages.find((line) => line.startsWith(':'))
                  const comment = commentLine ? commentLine.substring(1) : ''

                  const retryLine = messages.find((line) => line.startsWith('retry:'))
                  const retry = retryLine ? retryLine.substring(6) : ''

                  // 获取事件的内容
                  const dataLines = messages.filter((line) => line.startsWith('data:'))
                  //事件内容，合并多个data后保留换行符
                  const data = dataLines.map((line) => line.substring(5)).join('\n')
                  const eventObj: Event = {
                    id,
                    retry,
                    comment,
                    event: eventName,
                    data
                  }
                  config.sseCallback(eventObj, eventStr)
                  events.push(eventObj)
                }
              }
              response.data = events
            } finally {
              reader.releaseLock()
            }
          }
          return response
        })
      }
    }
    // 对于其他属性，返回它们的原始值
    return Reflect.get(target, prop, receiver)
  }
})

const axiosInstance = {
  async request<T = any, R = any>(config: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.request<any, AxiosResponse<any>, R>(config), config)
  },
  async get<T = any, R = any>(url: string, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.get<any, AxiosResponse<any>, R>(url, config), config)
  },
  async delete<T = any, R = any>(url: string, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.delete<any, AxiosResponse<any>, R>(url, config), config)
  },
  async head<T = any, R = any>(url: string, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.head<any, AxiosResponse<any>, R>(url, config), config)
  },
  async options<T = any, R = any>(url: string, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.options<any, AxiosResponse<any>, R>(url, config), config)
  },
  async post<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.post<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  async put<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.put<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  async patch<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.patch<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  async postForm<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.postForm<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  async putForm<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.putForm<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  async patchForm<T = any, R = any>(
    url: string,
    data?: R,
    config?: AxiosRequestConfig<R>
  ): Promise<T> {
    return process(() => proxyAxiosInstance.patchForm<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  //上传文件，Content-Type和超时时间需要单独配置
  async upload<T = any, R = any>(url: string, data?: R, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.upload<any, AxiosResponse<any>, R>(url, data, config), config)
  },
  //下载文件
  async download<T = any, R = any>(url: string, config?: AxiosRequestConfig<R>): Promise<T> {
    return process(() => proxyAxiosInstance.get<any, AxiosResponse<any>, R>(url, config), config)
  },
  //流式调用接口，SSE
  async sseRequest<T = any, R = any>(config: AxiosRequestConfig<R>): Promise<T> {
    return (await proxyAxiosInstance.sseRequest(config)).data
  }
}

async function process<R>(fun: () => Promise<AxiosResponse<any, any>>, config?: AxiosRequestConfig<R>) {
  if (config?.needLoading !== false) {
    //只有手动传入false时，才不会打开加载动画
    systemStore.openLoading()
  }
  try {
    const response = await fun()
    if (response.config.responseType !== 'stream') {
      const result = response.data
      if (result?.code === undefined) {
        return result
      } else {
        return result.data
      }
    } else {
      return response
    }
  } catch (e) {
    //出现异常，关闭所有加载状态
    systemStore.closeLoading(-1)
    throw e
  } finally {
    if (config?.needLoading !== false) {
      systemStore.closeLoading()
    }
  }
}

export default axiosInstance